package cn.edu.xust.communication.server.handler;


import cn.edu.xust.communication.model.Result;
import cn.edu.xust.communication.server.CommonOp;
import cn.edu.xust.communication.server.NettyServer;
import cn.edu.xust.communication.server.cache.ChannelMap;
import cn.edu.xust.communication.util.HexConverter;
import cn.edu.xust.communication.util.StringUtils;
import cn.edu.xust.entity.GatewayEntity;
import cn.edu.xust.service.DeviceService;
import cn.edu.xust.service.GatewayService;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 服务器逻辑，负责用于监听、处理各种I/O事件，并将设备传上来的数据写进数据库
 *
 * @author ：huangxin
 * @modified ：
 * @since ：2020/02/18 16:54
 */
@Component
@Slf4j
@ChannelHandler.Sharable
public class NettyServerDefaultHandler extends ChannelInboundHandlerAdapter {

    @Autowired
    DeviceService deviceService;
    @Autowired
    GatewayService gatewayService;
    /**
     * 电表号
     */
    private String ammeterId;
    /**
     * 开启定时采集任务标志
     */
    private boolean timingTaskLunch = true;
    /**
     * 同步锁
     */
    private CountDownLatch latch;

    /**
     * 消息的唯一ID
     */
    private String unidId = "";
    /**
     * 同步标志
     */
    private int rec;
    /**
     * 客户端返回的结果
     */
    private Result result = new Result();


    public NettyServerDefaultHandler() {

    }


    /**
     * 在客户端和服务器首次建立通信通道的时候触发次方法
     *
     * @param ctx 上下文
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        new Thread(() -> {
            Channel socketChannel = ctx.channel();
            /*将设备remoteAddress当作 map 的key*/
            String remoteAddress = socketChannel.remoteAddress().toString();
            if (ChannelMap.getDeviceFromMap(remoteAddress) == null) {
                ChannelMap.setChannelLock(remoteAddress, new ReentrantLock());
                ChannelMap.setDevicesMap(remoteAddress, ctx);
                log.info("客户端" + socketChannel.remoteAddress().toString() + " 成功建立连接！在线: " + ChannelMap.currentOnlineDevicesNum());
//                log.info("发送广播确认身份");
//                CommonOp.writeCommand(ctx.channel().remoteAddress().toString(), Dlt645Frame.BROADCAST_FRAME, UUID.randomUUID().toString());
            }
        }).start();
    }

    /**
     * 通道有读取事件时触发这个方法
     *
     * @param ctx 上下文对象，内部封装了许多有用的信息
     * @param msg 客户端发送过来的信息
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        new Thread(() -> {
            try {
                ByteBuf buffer = (ByteBuf) msg;
                //客户端IP
                String remoteAddress = ctx.channel().remoteAddress().toString();
                byte[] bytes = new byte[buffer.readableBytes()];
                //复制内容到字节数组
                buffer.readBytes(bytes);
                String hexString = HexConverter.receiveHexToString(bytes);//原文
                String hexchange = StringUtils.fromHexString(hexString);//转义
                //"1"的十六进制ASCII是31，十进制ASCII是49。
                log.info("有内容来啦！来源地址：{}，内容：{},转义:{}", remoteAddress, hexString, hexchange);
                result.setMessage(hexString);

                //注册包
                String zhuce1 = StringUtils.toHexString("zhuce1");
                //心跳包
                String tick2 = StringUtils.toHexString("tick2");


                if (hexString.equals(zhuce1)) {
                    log.info("回复注册包");
                    Result result = CommonOp.writeCommand(remoteAddress, zhuce1, UUID.randomUUID().toString());
                } else if (hexString.equals(tick2)) {
                    log.info("回复心跳包");
                    Result result = CommonOp.writeCommand(remoteAddress, tick2, UUID.randomUUID().toString());
                } else if (hexString.startsWith("FEFEFEFE68") && "68".equals(hexString.substring(22, 24))) {
                    log.info("识别为电表消息");
                } else if (hexString.startsWith("FEFEFE68")) {
                    log.info("识别为水表消息");
                } else if (hexString.length() == 34) {
                    if (hexchange.startsWith("FE FE FE")) {
                        log.info("长度34，且是FE FE FE开头，可能是成功执行");
                    } else {
                        log.info("长度34，且不是FE FE FE开头，可能是网关mac：{},，与ip进行绑定", hexchange);
                        ChannelMap.bindDeviceNumAndDeviceSocket(hexchange, remoteAddress);
                        GatewayEntity gateway = gatewayService.getByName(hexchange);
                        if (gateway == null) {
                            log.info("数据库未找到该网关:{}", hexchange);
                        } else {
                            gateway.setRealstate("1");
                            gateway.setIp(remoteAddress);
                            gateway.setUtime(new Date());
                            gatewayService.updateById(gateway);
                        }
                    }
                } else {
                    log.info("识别失败");
                }
                if (Objects.nonNull(latch)) {
                    if(!"00".equals(hexString)){
                        latch.countDown();
                    }else {
                        log.info("回复内容是00，不解同步锁，继续等待");
                    }
                }
                rec = 0;
            } catch (Exception e) {
                log.error("server error:" + e.getMessage());
                if (Objects.nonNull(latch)) {
                    latch.countDown();
                }
                rec = 0;
            }
        }).start();
    }


    /**
     * 通道的数据读取完毕后就会触发此方法
     *
     * @param ctx 上下文对象，内部封装了许多有用的信息
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //刷新缓存区
        ctx.flush();
    }


    /**
     * 通道断开事件触发此方法
     *
     * @param ctx 上下文
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("客户端 " + ctx.channel().remoteAddress() + "断开了连接");
        ChannelMap.removeDeviceFormMap(ctx.channel().remoteAddress().toString());
        //数据库设置状态
        GatewayEntity gateway = gatewayService.getByIP(ctx.channel().remoteAddress().toString());
        gateway.setRealstate("0");
        gateway.setIp("OFFLINE");
        gatewayService.updateById(gateway);
        /*异步的关闭和客户端的通信通道，以免浪费资源*/
        ctx.channel().closeFuture().sync();
    }

    /**
     * 发生异常时触发此方法
     *
     * @param ctx   上下文对象，内部封装了许多有用的信息
     * @param cause 异常信息
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error(cause.getMessage());
        ChannelMap.removeDeviceFormMap(ctx.channel().remoteAddress().toString());
        ctx.close();
    }



    public void resetSync(CountDownLatch latch, int rec) {
        this.latch = latch;
        this.rec = rec;
    }



    public void setUnidId(String s) {
        this.unidId = s;
    }

    public Result getResult() {
        return result;
    }

}



